package com.gaswell.aop.aspect;

import com.gaswell.aop.annotation.JHPermission;
import com.gaswell.aop.annotation.RedisCache;
import com.gaswell.service.ALLQjService;
import com.gaswell.utils.HttpContextUtils;
import com.gaswell.utils.JwtUtils;
import com.gaswell.utils.RedisEnumeration;
import com.gaswell.vo.Result;
import com.xiaow.utils.redis.RedisUtils;
import io.jsonwebtoken.Claims;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.time.Duration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Aspect
@Component
@Order(3)
public class RedisCacheAspect {

    @Autowired
    ALLQjService allQjService;

    @Autowired
    RedisTemplate redisTemplate;

    @Autowired
    RedisUtils redisUtils;


    @Pointcut("@annotation(com.gaswell.aop.annotation.RedisCache)")
    public void redisCachePointCut() {

    }

    @Around(value = "redisCachePointCut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {


        final Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;

        RedisCache annotation = methodSignature.getMethod().getAnnotation(RedisCache.class);
//      找到用于构建key的参数下标和前缀
        final int[] ints = annotation.argsIndexs();
        final String keyprefix = annotation.keyprefix();


//      获取接口的参数值
        final Object[] args = joinPoint.getArgs();

//      拼接key
        StringBuilder key = new StringBuilder(keyprefix);
        for (int anInt : ints) {
            key.append(args[anInt].toString());
        }

        final List range = redisTemplate.opsForList().range(key.toString(), 0, -1);
        if (range != null&&range.size()!=0) {
            Map o = (Map) range.get(range.size() - 1);
            return new Result(true, 200, "success", range.subList(0, range.size() - 1), (int) o.get("totalData"), (int) o.get("totalPage"));
        }

//      查询数据库并更新缓存
        return updateRedisCache(key.toString(), joinPoint, args);

    }


    public Result updateRedisCache(String key, ProceedingJoinPoint joinPoint, Object[] args) throws Throwable {
        try {
            Result result = null;
            //      这里需要加锁，防止所有人都查询数据库
            boolean b = redisUtils.tryLock(RedisEnumeration.MUTEXLOCK + key.toString());
            if (!b) {
                Thread.sleep(100);
                return updateRedisCache(key, joinPoint, args);
            }
            //      拿到锁了，需要在判断下有数据了吗
            final List range = redisTemplate.opsForList().range(key, 0, -1);
            if (range != null&&range.size()!=0) {
                Map o = (Map) range.get(range.size() - 1);
                return new Result(true, 200, "success", range.subList(0, range.size() - 1), (int) o.get("totalData"), (int) o.get("totalPage"));
            }
            //      如果没有更新，还是要查询数据库,并更新缓存
            result = (Result) joinPoint.proceed(args);


            final Integer total = result.getTotalData();
            final Integer totalPage = result.getTotalPage();
            final List final_list = (List) result.getData();

            HashMap<String, Integer> stringIntegerHashMap = new HashMap<>();
            stringIntegerHashMap.put("totalData", total);
            stringIntegerHashMap.put("totalPage", totalPage);


            redisTemplate.opsForList().rightPushAll(key, final_list);
            redisTemplate.opsForList().rightPush(key, stringIntegerHashMap);
            redisTemplate.expire(key, Duration.ofDays(1));
            return result;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {
            redisUtils.unlock(RedisEnumeration.MUTEXLOCK + key);
        }
    }


}